package com.gitee.kenewstar.utils;

import com.gitee.kenewstar.exception.CommonException;
import io.minio.MinioClient;
import io.minio.ObjectStat;
import io.minio.PutObjectOptions;
import io.minio.Result;
import io.minio.http.Method;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteError;
import io.minio.messages.Item;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;

/**
 * <p>
 *     minio云存储工具类
 * </p>
 * <p>
 *     参考文档地址：http://docs.minio.org.cn/docs/master/java-client-api-reference
 * </p>
 * @author kenewstar
 * @date 2021/7/7
 */
public abstract class MinioUtil {

	private static final MinioClient MINIO;
	private static final Logger LOG = Logger.getLogger(MinioUtil.class.getName());

	private MinioUtil() {}

	static {
		try {
			MINIO = new MinioClient(BaseInfo.END_POINT,
					BaseInfo.PORT,
					BaseInfo.ACCESS_KEY,
					BaseInfo.ACCESS_SECRET);
			LOG.info("bucket client init completed ...");
		} catch (Exception e) {
			LOG.info("bucket client init failed ...");
			throw new CommonException("Minio client create failed ", e);
		}
	}

	public interface BaseInfo {

		String END_POINT = "http://127.0.0.1";
		int    PORT      = 9000;
		String ACCESS_KEY = "minioadmin";
		String ACCESS_SECRET = "minioadmin";
		int    EXPIRES_TIME = 7 * 24 * 3600;

	}

	public static MinioClient getInstance() {
		return MINIO;
	}

	/**
	 * <p>
	 *     校验存储桶是否存在
	 * </p>
	 * @param bucketName 存储桶的名称
	 * @return true -> 存在 | false -> 不存在
	 */
	public static boolean validateBucket(String bucketName) {
		boolean flag;
		try {
			flag = MINIO.bucketExists(bucketName);
		} catch (Exception e) {
			throw new CommonException(e);
		}
		return flag;
	}

	/**
	 * <p>
	 *     校验文件是否存在
	 * </p>
	 * @param bucketName 存储桶名称
	 * @param fileName 文件名
	 * @return 文件的信息对象[ObjectStat]
	 */
	public static ObjectStat validateFile(String bucketName, String fileName) {
		ObjectStat flag;
		try {
			flag = MINIO.statObject(bucketName, fileName);
		} catch (Exception e) {
			throw new CommonException(e);
		}
		return flag;
	}

	/**
	 * <p>
	 *     创建存储桶
	 * </p>
	 * @param bucketName 存储桶的名称
	 */
	public static void createBucket(String bucketName) {
		boolean exists = validateBucket(bucketName);
		try {
			if (exists) {
				throw new CommonException("bucket[" + bucketName + "] is exists");
			}
			MINIO.makeBucket(bucketName);
			LOG.info("created bucket -> " + bucketName);
		} catch (Exception e) {
			LOG.info("create bucket[" + bucketName + "] failed ...");
			throw new  CommonException("bucket create failed", e);
		}
	}

	/**
	 * <p>
	 * 		列出所有存储桶
	 * </p>
	 *
	 * @return 桶对象的集合
	 */
	public static List<Bucket> bucketList() {
		List<Bucket> bucketList;
		try {
			bucketList = MINIO.listBuckets();
		} catch (Exception e) {
			throw new CommonException("list bucket failed", e);
		}
 		return bucketList;
	}

	/**
	 * <p>
	 *     删除存储桶
	 * </p>
	 * <p>
	 *     如果桶不存在, 则抛出异常
	 * </p>
	 * <p>
	 *     如果桶的内容不为空，则抛出异常
	 * </p>
	 * @param bucketName 存储桶名称
	 */
	public static void removeBucket(String bucketName) {
		try {
			MINIO.removeBucket(bucketName);
		} catch (Exception e) {
			throw new CommonException("remove bucket[" + bucketName + "] failed", e);
		}
	}

	/**
	 * <p>
	 *     查询存储桶文件列表中的所有文件
	 * </p>
	 * @param bucketName 存储桶的名称
	 * @return 文件列表集合
	 */
	public static Iterable<Result<Item>> fileList(String bucketName) {
		Iterable<Result<Item>> objects;
		try {
			objects = MINIO.listObjects(bucketName);
		} catch (Exception e) {
			throw new CommonException("fileList query failed", e);
		}
		return objects;
	}

	/**
	 * <p>
	 *     获取存储桶中指定的文件对象的下载流
	 * </p>
	 * @param bucketName 存储桶名称
	 * @param fileName 文件名称
	 * @return 输入流
	 */
	public static InputStream download(String bucketName, String fileName) {
		InputStream is;
		try {
			// 校验文件是否存在
			validateFile(bucketName, fileName);
			is = MINIO.getObject(bucketName, fileName);
		} catch (Exception e) {
			throw new CommonException("download failed", e);
		}
		return is;
	}

	/**
	 * <p>
	 *     将输入流写入到本地文件
	 * </p>
	 * @param is 输入流
	 * @param dest 本地地址 -> [ D://1.png ]
	 */
	public static void fileWriteLocal(InputStream is, String dest) {
		int index;
		byte[] bytes = new byte[1024];
		try {
			FileOutputStream fis = new FileOutputStream(dest);
			while ((index = is.read(bytes)) != -1) {
				fis.write(bytes, 0, index);
				fis.flush();
			}
			fis.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}

	/**
	 * <p>
	 *     上传文件
	 * </p>
	 * @param bucketName 存储桶名称
	 * @param fileName 文件名称
	 * @param is 输入流
	 * @param contentType 内容类型
	 */
	public static void upload(String bucketName, String fileName, InputStream is, String contentType) {
		try {
			LOG.info("upload file size -> " + CommonUtil.byte2Size(is.available()));
			PutObjectOptions options = new PutObjectOptions(is.available(), -1);
			options.setContentType(contentType);
			MINIO.putObject(bucketName, fileName, is, options);
		} catch (Exception e) {
			throw new CommonException("file upload failed", e);
		} finally {
			try {
				is.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}

	}

	public static void upload(String bucketName, String fileName, InputStream is) {
		upload(bucketName, fileName, is, "application/octet-stream");
	}

	/**
	 * <p>
	 *     文件上传
	 * </p>
	 * @param bucketName 存储桶
	 * @param fileName 文件名
	 * @param srcFileName 本地文件路径
	 * @param contentType 内容类型
	 */
	public static void upload(String bucketName, String fileName, String srcFileName, String contentType) {
		try {
			File file = new File(srcFileName);
			LOG.info("upload file size -> " + CommonUtil.byte2Size(file.length()));
			PutObjectOptions options = new PutObjectOptions(file.length(), -1);
			options.setContentType(contentType);
			MINIO.putObject(bucketName, fileName, srcFileName, options);
		} catch (Exception e) {
			throw new CommonException("upload file failed", e);
		}
	}

	public static void upload(String bucketName, String fileName, String srcFileName) {
		upload(bucketName, fileName, srcFileName, "application/octet-stream");
	}

	/**
	 * <p>
	 *     删除文件
	 * </p>
	 * @param bucketName 存储桶名称
	 * @param fileName 文件名称
	 */
	public static void delete(String bucketName, String fileName) {
		try {
			validateFile(bucketName, fileName);
			MINIO.removeObject(bucketName, fileName);
			LOG.info("delete bucket[" + bucketName + "] file[" + fileName + "] success");
		} catch (Exception e) {
			throw new CommonException("delete file failed" ,e);
		}
	}

	/**
	 * <p>
	 *     批量删除文件
	 * </p>
	 * @param bucketName 存储桶名称
	 * @param fileNameList 文件列表集合
	 */
	public static Iterable<Result<DeleteError>> batchDelete(String bucketName, List<String> fileNameList) {
		Iterable<Result<DeleteError>> iterable;
		try {
			iterable = MINIO.removeObjects(bucketName, fileNameList);
			LOG.info("batch delete file success");
		} catch (Exception e) {
			throw new  CommonException("batch delete file failed" , e);
		}
		return iterable;
	}

	/**
	 * <p>
	 *     生成在线访问文件的url地址
	 * </p>
	 * @param bucketName 存储桶名
	 * @param fileName 文件名
	 * @param expires 过期时间 (s) 不得大于 7 天
	 * @param reqParams 请求参数(可选)
	 * @return http get url
	 */
	public static String generateHttpGetUrl(String bucketName, String fileName, int expires, Map<String, String> reqParams) {
		String url;
		try {
			url = MINIO.presignedGetObject(bucketName, fileName, expires, reqParams);
		} catch (Exception e) {
			throw new CommonException("http url generate url failed", e);
		}
		return url;
	}

	/**
	 * <p>
	 * 	   生成在线访问文件的url地址
	 * </p>
	 * <p>
	 *     默认过期时间 7 天
	 * </p>
	 * @param bucketName 存储桶名
	 * @param fileName 文件名
	 * @return http get url
	 */
	public static String generateHttpGetUrl(String bucketName, String fileName) {
		return generateHttpGetUrl(bucketName, fileName, BaseInfo.EXPIRES_TIME);
	}

	/**
	 * <p>
	 *     生成在线访问文件的url地址
	 * </p>
	 * @param bucketName 存储桶名称
	 * @param fileName 文件名
	 * @param expires 过期时间 (s) 不得大于 7 天
	 * @return http get url
	 */
	public static String generateHttpGetUrl(String bucketName, String fileName, int expires) {
		return generateHttpGetUrl(bucketName, fileName, expires, null);
	}


	public static String generateHttpPutUrl(String bucketName, String fileName, int expires, Map<String, String> reqParams) {
		String url;
		try {
			url = MINIO.getPresignedObjectUrl(Method.PUT, bucketName, fileName, expires, reqParams);
		} catch (Exception e) {
			throw new CommonException("http url generate url failed", e);
		}
		return url;
	}

	public static String generateHttpPutUrl(String bucketName, String fileName, int expires) {
		return generateHttpPutUrl(bucketName, fileName, expires, null);
	}

	public static String generateHttpPutUrl(String bucketName, String fileName) {
		return generateHttpPutUrl(bucketName, fileName, BaseInfo.EXPIRES_TIME);
	}
}
